<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Controls III</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="winapi, Windows API, C, no MFC, Windows Controls">
<meta name="description" content="In this part of the Winapi tutorial we continue covering
Windows controls.">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Controls III</h1>


<p>
In this chapter, we will finish talking about Winapi controls. We will 
mention radio buttons, combo box and a progress bar. 
</p>

<script type="text/javascript"><!--
google_ad_client = "ca-pub-9706709751191532";
/* LargeSquare */
google_ad_slot = "5070696322";
google_ad_width = 336;
google_ad_height = 280;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>

<h2>Radio buttons and GroupBox</h2>

<p>
Here we introduce two controls. A group box is a rectangle that surrounds a set of controls.
These are often radio buttons. A group box has a label, that describes the control. The purpose of this
control is to group controls, that are somehow related.
A radio button is a special kind of button, that can be selected by the user, but not cleared.
It allows the user to select a single exclusive choice from a group of options.
</p>




<pre class="code">
#include &lt;windows.h&gt;

#define ID_BLUE 1
#define ID_YELLOW 2
#define ID_ORANGE 3

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE g_hinst;
COLORREF g_color;


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow)
{
  HWND hwnd;
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT("GroupBox");
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);

  g_hinst = hInstance;
  
  RegisterClass(&amp;wc);
  hwnd = CreateWindow(wc.lpszClassName, TEXT("GroupBox"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 300, 170, 0, 0, hInstance, 0);  


  while( GetMessage(&amp;msg, NULL, 0, 0)) {
    DispatchMessage(&amp;msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
  HDC hdc;
  PAINTSTRUCT ps;
  HBRUSH hBrush, holdBrush;
  HPEN hPen, holdPen;

  switch(msg)  
  {
      case WM_CREATE:
          CreateWindow(TEXT("button"), TEXT("Choose Color"), 
                WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
                10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL);
          CreateWindow(TEXT("button"), TEXT("Blue"),
                WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
                20, 30, 100, 30, hwnd, (HMENU)ID_BLUE , g_hinst, NULL);
          CreateWindow(TEXT("button"), TEXT("Yellow"),
                WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
                20, 55, 100, 30, hwnd, (HMENU)ID_YELLOW , g_hinst, NULL);
          CreateWindow(TEXT("button"), TEXT("Orange"),
                WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
                20, 80, 100, 30, hwnd, (HMENU)ID_ORANGE , g_hinst, NULL);
      
          break;

      case WM_COMMAND:
	   if (HIWORD(wParam) == BN_CLICKED) {
	       switch (LOWORD(wParam)) {
		    case ID_BLUE:
		        g_color = RGB(0, 76, 255);
		        break;
		    case ID_YELLOW:
		        g_color = RGB(255, 255, 0);
		        break;
		    case ID_ORANGE:
		        g_color = RGB(255, 123, 0);
		        break;
		}
	       InvalidateRect(hwnd, NULL, TRUE);
	   }
          break;

      case WM_PAINT:
          hdc = BeginPaint(hwnd, &amp;ps);
          hBrush = CreateSolidBrush(g_color);
          hPen = CreatePen(PS_NULL, 1, RGB(0, 0, 0));
          holdPen = SelectObject(hdc, hPen);
          holdBrush = (HBRUSH) SelectObject(hdc, hBrush);
	
          Rectangle(hdc, 160, 20, 260, 120);

          SelectObject(hdc, holdBrush);
          SelectObject(hdc, holdPen);
          DeleteObject(hPen);
          DeleteObject(hBrush);
          EndPaint(hwnd, &amp;ps);
          break;

      case WM_DESTROY:
          PostQuitMessage(0);
          break; 
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}
</pre>

<p>
In our example, we have a group box with three radio buttons. By clicking on 
the radio button, we select a background color for 
the rectangle on the right. 
</p>

<pre class="explanation">
 CreateWindow(TEXT("button"), TEXT("Choose Color"), 
       WS_CHILD | WS_VISIBLE | BS_GROUPBOX,
       10, 10, 120, 110, hwnd, (HMENU) 0, g_hinst, NULL);
</pre>

<p>
A group box is a special kind of a button with <i>BS_GROUPBOX</i> style.
</p>

<pre class="explanation">
 CreateWindow(TEXT("button"), TEXT("Blue"),
       WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
       20, 30, 100, 30, hwnd, (HMENU)ID_BLUE , g_hinst, NULL);
</pre>

<p>
A radiobutton is also only a special kind of a button with <i>BS_AUTORADIOBUTTON</i> style.
</p>

<pre class="explanation">
 case ID_BLUE:
     g_color = RGB(0, 76, 255);
     break;
</pre>

<p>
If we click on the radio button, we fill a global variable with a selected color. 
This variable will be used to create a brush, that will
fill the rectangle.
</p>

<pre class="explanation">
 InvalidateRect(hwnd, NULL, TRUE);
</pre>

<p>
 We invalidate the rectangle (in this case whole window), which will cause the 
client area to be redrawn. This will launch a <i>WM_PAINT</i> message.
During the <i>WM_PAINT</i> message, we draw the rectangle. Drawing is explained 
in <a href="../gdi">GDI</a> chapter in more detail.
</p>

<br>
<img src="/img/gui/winapi/groupbox.jpg" alt="GroupBox, Radio boxes">
<div class="figure">Figure: GroupBox, Radio boxes</div>


<h3>ComboBox</h3>

<p> 
A combo box is a combination of an edit box or static text and a list. 
A combo box is used when we need to select an item from 
a list of available options.
</p>

<pre class="code">
#include &lt;windows.h&gt;


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

HINSTANCE g_hinst;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow)
{
  HWND hwnd;
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT("Application");
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);

  g_hinst = hInstance;
  
  RegisterClass(&amp;wc);
  hwnd = CreateWindow(wc.lpszClassName, TEXT("Combo Box"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 270, 170, 0, 0, hInstance, 0);  


  while( GetMessage(&amp;msg, NULL, 0, 0)) {
    DispatchMessage(&amp;msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{

  static HWND hwndCombo, hwndStatic;
  const TCHAR *items[] = { TEXT("FreeBSD"), TEXT("OpenBSD"), 
      TEXT("Ubuntu"), TEXT("Solaris") };
  int i;
  LRESULT sel = 0;

  switch(msg)  
  {
      case WM_CREATE:
            hwndCombo = CreateWindow(TEXT("combobox"), NULL, 
                  WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
                  10, 10, 120, 110, hwnd, NULL, g_hinst, NULL);   

            CreateWindow(TEXT("button"), TEXT("Drop down"), 
                  WS_CHILD | WS_VISIBLE,
                  150, 10, 90, 25, hwnd, (HMENU)1, g_hinst, NULL); 

            hwndStatic = CreateWindow(TEXT("static"), TEXT(""), 
                  WS_CHILD | WS_VISIBLE,
                  150, 80, 90, 25, hwnd, NULL, g_hinst, NULL); 

            for ( i = 0; i < 4; i++ ) {
                SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
            }

            break;

      case WM_COMMAND:
           if (HIWORD(wParam) == BN_CLICKED) {
                SendMessage(hwndCombo, CB_SHOWDROPDOWN, (WPARAM) TRUE, 0);
           }
             
           if ( HIWORD(wParam) == CBN_SELCHANGE) {               
                sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
                SetWindowText(hwndStatic, items[sel]);
                SetFocus(hwnd);
           }

           break;

      case WM_DESTROY:
          PostQuitMessage(0);
          break; 
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}
</pre>

<p>
In our example, we put three controls on the window. A combo box, a button and 
a static text. The static text displays the currently selected item from the 
combo box. It is used to demonstrate the <i>CBN_SELCHANGE</i> combo box message. 
The button programatically opens the combo box. 
</p>

<pre class="explanation">
 hwndCombo = CreateWindow(TEXT("combobox"), NULL, 
     WS_CHILD | WS_VISIBLE | CBS_DROPDOWN,
     10, 10, 120, 110, hwnd, NULL, g_hinst, NULL);   
</pre>

<p>
To create a combo box, we use the combobox string. We use the <i>CBS_DROPDOWN</i> flag.
</p>

<pre class="explanation">
 for ( i = 0; i < 4; i++ ) {
     SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) items[i]);
 }
</pre>

<p>
We fill the combo box with items. To add a string to the combo box, we 
send a <i>CB_ADDSTRING</i> message.
</p>

<p>
If we select an item from the combo box, the window procedure receives the 
<i>WM_COMMAND</i> message with the notification message <i>CBN_SELCHANGE</i> in the
high-order word of the wParam parameter.
</p>

<pre class="explanation">
 sel = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0);
 SetWindowText(hwndStatic, items[sel]);
 SetFocus(hwnd);
</pre>

<p>
We figure out the currently selected item. We send a <i>CB_GETCURSEL</i> 
message to the combo box. The function returns the index of the currently selected item.
We set the static text to the currently selected string. Finally, we set focus 
to the main window. By default the combo box has the focus. But it looks ugly, so 
I changed programatically the focus. 
</p>


<br>
<img src="/img/gui/winapi/combobox.jpg" alt="Combo box">
<div class="figure">Figure: Combo box</div>

<h2>Progress bar</h2>

<p> 
A progress bar is a control that is used, when we process lengthy tasks. 
It is animated so that the user knows, that our task is progressing.
</p>


<pre class="code">
#include &lt;windows.h&gt;
#include &lt;commctrl.h&gt;

#define ID_BUTTON 1
#define ID_TIMER 2

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE g_hinst;


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow)
{

  HWND hwnd;
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT("Application");
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);

  g_hinst = hInstance;
  
  RegisterClass(&amp;wc);
  hwnd = CreateWindow(wc.lpszClassName, TEXT("Progress bar"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 260, 170, 0, 0, hInstance, 0);  


  while( GetMessage(&amp;msg, NULL, 0, 0)) {
    DispatchMessage(&amp;msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{

  static HWND hwndPrgBar;
  static int i = 1;
 
  INITCOMMONCONTROLSEX InitCtrlEx;

  InitCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
  InitCtrlEx.dwICC  = ICC_PROGRESS_CLASS;
  InitCommonControlsEx(&amp;InitCtrlEx);

  switch(msg)  
  {
      case WM_CREATE:
          hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL, 
                WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
                30, 20, 190, 25, hwnd, NULL, g_hinst, NULL);   

          CreateWindow(TEXT("button"), TEXT("Start"), 
                WS_CHILD | WS_VISIBLE,
                85, 90, 80, 25, hwnd, (HMENU) 1, g_hinst, NULL);  

          SendMessage(hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150));
          SendMessage(hwndPrgBar, PBM_SETSTEP, 1, 0 );
          break;


      case WM_TIMER:
          SendMessage( hwndPrgBar, PBM_STEPIT, 0, 0 );
          i++;
          if ( i == 150 ) 
              KillTimer(hwnd, ID_TIMER);
          break;
              
      case WM_COMMAND:
          i = 1;
          SendMessage( hwndPrgBar, PBM_SETPOS, 0, 0 );
          SetTimer(hwnd, ID_TIMER, 5, NULL);
          break;

      case WM_DESTROY:
          KillTimer(hwnd, ID_TIMER);
          PostQuitMessage(0);
          break; 
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}
</pre>

<p>
In our example, we have a progress bar and a button. The button (re)starts to 
progress bar control. We use a timer to update the progress bar.
</p>

<pre class="explanation">
 hwndPrgBar = CreateWindowEx(0, PROGRESS_CLASS, NULL, 
     WS_CHILD | WS_VISIBLE | PBS_SMOOTH,
     30, 20, 190, 25, hwnd, NULL, g_hinst, NULL);  
</pre>

<p>
We create a progress bar control with <i>PROGRESS_CLASS</i> class name and <i>PBS_SMOOTH</i> style.
</p>

<pre class="explanation">
 SendMessage( hwndPrgBar, PBM_SETRANGE, 0, MAKELPARAM(0, 150));
 SendMessage( hwndPrgBar, PBM_SETSTEP, 1, 0 );
</pre>

<p>
We set the range of the progress bar and it's step. 
</p>

<pre class="explanation">
 i = 1;
 SendMessage( hwndPrgBar, PBM_SETPOS, 0, 0 );
 SetTimer(hwnd, ID_TIMER, 5, NULL);
</pre>

<p>
When we press the start button, we set the i value to 1, set the initial 
position of the progress bar and start the timer. The timer will
send periodically a <i>WM_TIMER</i> message to the window procedure. 
Until it is killed.
</p>

<pre class="explanation">
 case WM_TIMER:
     SendMessage( hwndPrgBar, PBM_STEPIT, 0, 0 );
      i++;
      if ( i == 150 ) 
          KillTimer(hwnd, ID_TIMER);
      break;
</pre>

<p>
During the <i>WM_TIMER</i> message, we update the progress bar by one 
step sending the <i>PBM_STEPIT</i> message. We kill the timer, when the
progress bar stops processing.
</p>

<img src="/img/gui/winapi/progressbar.jpg" alt="Progress bar">
<div class="figure">Figure: Progress bar</div>


<p>
In this part of the Winapi tutorial, we have finished covering Winapi controls. 
</p>


<div class="center"> 
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* horizontal */
google_ad_slot = "1734478269";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 
</div> 
<br> 

<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>


<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified July 24, 2007  <span class="copyright">&copy; 2007 - 2013 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>

